<?php
declare (strict_types = 1);

namespace app;

use think\App;
use think\exception\ValidateException;
use think\Validate;

/**
 * 控制器基础类
 */
abstract class BaseController
{
    /**
     * Request实例
     * @var \think\Request
     */
    protected $request;

    /**
     * 应用实例
     * @var \think\App
     */
    protected $app;

    /**
     * 是否批量验证
     * @var bool
     */
    protected $batchValidate = false;

    /**
     * 控制器中间件
     * @var array
     */
    protected $middleware = [];

    /**
     * 用户信息
     * @var mixed
     */
    protected $user;
    
    /**
     * 构造方法
     * @access public
     * @param  App  $app  应用对象
     */
    public function __construct(App $app)
    {
        $this->app     = $app;
        $this->request = $this->app->request;
        $this->user = $this->request->middleware('user',null);

        // 控制器初始化
        $this->initialize();
    }

    // 初始化
    protected function initialize()
    {}

    /**
     * 验证数据
     * @access protected
     * @param  array        $data     数据
     * @param  string|array $validate 验证器名或者验证规则数组
     * @param  array        $message  提示信息
     * @param  bool         $batch    是否批量验证
     * @return array|string|true
     * @throws ValidateException
     */
    protected function validate(array $data, $validate, array $message = [], bool $batch = false)
    {
        if (is_array($validate)) {
            $v = new Validate();
            $v->rule($validate);
        } else {
            if (strpos($validate, '.')) {
                // 支持场景
                [$validate, $scene] = explode('.', $validate);
            }
            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
            $v     = new $class();
            if (!empty($scene)) {
                $v->scene($scene);
            }
        }

        $v->message($message);

        // 是否批量验证
        if ($batch || $this->batchValidate) {
            $v->batch(true);
        }

        return $v->failException(true)->check($data);
    }

    /**上传函数 */
    protected function upload()
    {   
        $filename = 'file';
        $path = 'common';
        $isComplete = false;
        $ext = '';
        try {
            $file = request()->file($filename);
            if (empty($file)) {
                throw new \Exception('没有文件上传');
            }
            if ($this->request->request('chunkid')) 
            {
                $chunkid = $this->request->request('chunkid');
                $chunkindex = $this->request->request('sectionNum');
                $chunkcount = $this->request->request('sectionTotal');
                $fileName = $chunkid . "-" . $chunkindex . '.tmp';
                $path = $path . DIRECTORY_SEPARATOR . date('Ymd');
                $filepath = \think\facade\Filesystem::disk('public')->putFileAs($path, $file, $fileName);
                
                // 合并
                if ($chunkindex + 1 == $chunkcount) {
                    $filePath = config('filesystem.disks.public.root') . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR;
                    $completed = true;
                    //检查所有分片是否都存在
                    for ($i = 0; $i < $chunkcount; $i++) {
                        if (!file_exists("{$filePath}{$chunkid}-{$i}.tmp")) {
                            $completed = false;
                            break;
                        }
                    }
                    // 删除
                    if(!$completed) {
                        for ($i = 0; $i < $chunkcount; $i++) {
                            if (file_exists("{$filePath}{$chunkid}-{$i}.tmp")) {
                                @unlink("{$filePath}{$chunkid}-{$i}.tmp"); //删除分片
                            }
                        }       
                        throw new \Exception("分片上传文件错误");
                    }else{
                        $isComplete = true;
                    }
                    
                    // uniapp 上传会拿不到文件扩展名
                    $ext = $file->getOriginalExtension() ? $file->getOriginalExtension() : substr(strrchr($this->request->post('rawName'), '.'), 1);
                    if (!$destFile = @fopen($filePath . $chunkid . '.' . $ext, "wb")) {
                        throw new \Exception("分片上传文件错误.");
                    }
                    
                    if (flock($destFile, LOCK_EX)) { // 进行排他型锁定
                        for ($i = 0; $i < $chunkcount; $i++) {
                            $partFile = "{$filePath}{$chunkid}-{$i}.tmp";
                            if (!$handle = @fopen($partFile, "rb")) {
                                break;
                            }
                            while ($buff = fread($handle, filesize($partFile))) {
                                fwrite($destFile, $buff);
                            }
                            @fclose($handle);
                            @unlink($partFile); //删除分片
                        }
                        
                        flock($destFile, LOCK_UN);
                    }
                    @fclose($destFile);
                    
                    $filepath = '/storage/' . str_replace(DIRECTORY_SEPARATOR, '/', $path) . '/' . $chunkid . '.' . $ext;
                    $url = $filepath;
                    $isComplete = true;
                }
            } else {
                $upload = config('filesystem.config');
                
                validate(['file'=> [
                    'fileSize' => $upload['fileSize'],
                    'fileExt' => $upload['fileExt'],
                    //'fileMime' => $upload['fileMime'],
                ]], [
                    'file.fileSize' => '上传文件不能大于'. $upload['fileSize'] / 1024 / 1024 .'M',
                ])->check(['file' => $file]);
                if (is_array($file)) {
                    $url = [];
                    $filepath = [];
                    foreach ($file as $value) {
                        $upload_path = \think\facade\Filesystem::disk('public')->putFile($path, $value);
                        $url[] = request()->domain() . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;            
                        $filepath[] = $upload_path;
                        $source[] = app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;
                        event('upload_success', ['source' => app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path, 'file' => $file]);
                    }
                } else {
                    $upload_path = \think\facade\Filesystem::disk('public')->putFile($path, $file);
                    $url = request()->domain() . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;
                    $filepath = $upload_path;
                    $source = app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;
                    event('upload_success', ['source' => app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path, 'file' => $file]);
                }
                $ext = $file->getOriginalExtension();
                $isComplete = true;
            }
        } catch (\think\exception\ValidateException $e) {
            throw new \Exception($e->getMessage());
            
        } catch(\Exception $e) {
            throw new \Exception($e->getMessage());
            
        }
        $title = $file = request()->param('title');
        return ['filepath' => $filepath, 'url' => $url ?? '' ,'title'=>$title,'isComplete'=>$isComplete,'ext'=>$ext];
    }

}
